// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.IO.Compression;
using System.Transactions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;

namespace SnapObjects.Data.AspNetCore
{
    /// <summary>
    ///     Provides extended features for developing RESTFul Web services based on the ASP.NET Web APIs and integrating 
    ///     with the SnapObjects runtime features.
    /// </summary>
    public static class WebExtensions
    {
        /// <summary>
        ///     Enables the actions in the controller to support the parameters of the type IDataUnpacker,
        ///     <see cref="IModelEntry{T}"/> and <see cref="IEnumerable{IModelEntry{T}}"/>.
        /// </summary>
        /// <param name="options">
        ///     The MvcOptions object which provides programmatic configuration for the MVC framework.
        /// </param>
        /// <returns>Returns the MvcOptions object.</returns>
        public static MvcOptions UseCoreIntegrated(this MvcOptions options)
        {
            options.ModelBinderProviders.Insert(0, new ModelBinderProvider());

            options.OutputFormatters.Insert(0, new DataPackerJsonOutputFormatter());
            options.OutputFormatters.Insert(0, new DataPackerXmlOutputFormatter());
            options.OutputFormatters.Insert(0, new DynamicModelJsonOutputFormatter());
            options.OutputFormatters.Insert(0, new DynamicModelXmlOutputFormatter());

            return options;
        }

        /// <summary>
        ///     Uses the TransactionScope instance to automatically manage transactions for all actions in all controllers.
        /// </summary>
        /// <param name="options">
        ///     The MvcOptions object which provides programmatic configuration for the MVC framework.
        /// </param>
        /// <param name="scopeTimeout">
        ///     The number of the millisecond after which the transaction scope times out and aborts the transaction.
        ///     Default is 0.
        /// </param>
        /// <param name="isolationLevel">
        ///     An IsolationLevel enumeration that specifies the isolation level of transaction.
        ///     Default is IsolationLevel.Serializable.
        /// </param>
        /// <param name="asyncFlowOption">
        ///     A TransactionScopeAsyncFlowOption enumeration that describes whether the ambient transaction associated with
        ///     the transaction scope will flow across thread continuations when using Task or async/await .NET async 
        ///     programming patterns. Default is TransactionScopeAsyncFlowOption.Enabled.
        /// </param>
        /// <param name="commitPolicy">
        ///     A CommitPolicy enumeration that specifies when to commit the transaction. 
        ///     Default is CommitPolicy.OnActionExecuted.
        /// </param>
        /// <returns>
        ///     Returns the MvcOptions object which provides programmatic configuration for the MVC framework.
        /// </returns>
        public static MvcOptions UseTransaction(
            this MvcOptions options,
            double scopeTimeout = 0,
            IsolationLevel isolationLevel = IsolationLevel.Serializable,
            TransactionScopeAsyncFlowOption asyncFlowOption = TransactionScopeAsyncFlowOption.Enabled,
            CommitPolicy commitPolicy = CommitPolicy.OnActionExecuted)
        {
            var attribute = new TransactionAttribute
            {
                CommitPolicy = commitPolicy,
                ScopeTimeout = scopeTimeout,
                IsolationLevel = isolationLevel,
                AsyncFlowOption = asyncFlowOption
            };

            options.Filters.Add(attribute);

            return options;
        }

        /// <summary>
        ///     Adds the GZIP response compression service and specifies the compression level.
        /// </summary>
        /// <param name="services">
        ///     The Microsoft.Extensions.DependencyInjection.IServiceCollection for adding services.
        /// </param>
        /// <param name="compressionLevel">
        ///     The level of compression for the stream.
        /// </param>
        /// <returns>
        ///     Returns the current Microsoft.Extensions.DependencyInjection.IServiceCollection object.
        /// </returns>
        public static IServiceCollection AddGzipCompression(
            this IServiceCollection services, CompressionLevel compressionLevel)
        {
            services.AddResponseCompression(m =>
            {
                m.EnableForHttps = true;
                //m.MimeTypes = new string[] { "application/json", "application/xml" };
                m.Providers.Add(new GzipCompressionProvider(
                    new GzipCompressionProviderOptions() { Level = compressionLevel }));
            });

            return services;
        }
    }
}
